package com.idea.plugin.core.resolver;

import com.idea.plugin.core.constant.InitConstant;
import com.idea.plugin.core.domain.dto.JavaMethodDTO;
import com.idea.plugin.core.domain.dto.JavaParameterDTO;
import com.idea.plugin.core.domain.info.JavaClassInfo;
import com.idea.plugin.core.handler.FullNameHandle;
import com.idea.plugin.core.helper.JavaClassHelper;
import com.idea.plugin.core.helper.StringUtils;
import com.idea.plugin.core.logging.Log;
import com.idea.plugin.core.logging.SystemStreamLog;
import com.thoughtworks.qdox.model.JavaAnnotation;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaConstructor;
import com.thoughtworks.qdox.model.JavaField;
import com.thoughtworks.qdox.model.JavaMethod;
import com.thoughtworks.qdox.model.JavaParameter;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 构建方法的参数
 *
 * @author chenhx
 * @version BuildClassMethodParameteImpl.java, v 0.1 2019-06-27 16:51 chenhx
 */
public class UnitTestingMethodParameterResolver {
    private static Log log = new SystemStreamLog();

    /**
     * 获取的参数
     * 参数组装-方法参数
     *
     * @param javaMethod    类方法信息
     * @param javaClassInfo 存储的类信息
     * @param javaMethodDTO 模板方法信息
     * @param javaClass     当前方法的所在类
     */
    public static void build(JavaMethod javaMethod, JavaClassInfo javaClassInfo, JavaMethodDTO javaMethodDTO, JavaClass javaClass) {
        List<JavaParameterDTO> javaParameterDTOS = new ArrayList<>();

        List<JavaParameter> parameterList = javaMethod.getParameters();
        int order = 1;
        //这里是方法最开始的第一层参数
        for (JavaParameter javaParameter : parameterList) {
            JavaParameterDTO javaParameterDTO = new JavaParameterDTO();
            javaParameterDTO.setName(javaParameter.getName());
            //参数的全限定名称
            String typeToStr = javaParameter.getType().getFullyQualifiedName();
            //设置全称
            javaParameterDTO.setFullyType(typeToStr);
            String type = InitConstant.getAbbreviation(typeToStr);
            //设置简称
            javaParameterDTO.setType(type);
            javaParameterDTO.setCustomType(false);

            //进行设置导入包，全称与简称 的处理
            FullNameHandle.addQualifiedNameToImportPackageMap(javaParameterDTO, javaClassInfo.getImportJavaPackageMap());

            //设置构造参数需要的值
            javaParameterDTO.setClassfullyType(javaClass.getFullyQualifiedName());
            javaParameterDTO.setMethodfullyType(javaClass.getFullyQualifiedName() + "#" + javaMethod.getName());
            javaParameterDTO.setPackageName(javaClass.getPackageName());
            log.info("当前的参数信息: " + javaParameterDTO);

            //设置默认值
            javaParameterDTOSetValue(javaParameterDTO, InitConstant.getValue(type));
            javaParameterDTO.setOrder(order++);

            // 如果没有默认值，需要进行初始化
            if (javaParameterDTO.getValue() == null) {
                JavaClass parameterJavaClass = JavaClassHelper.findClassByQualifiedName(typeToStr);
                log.info("自定义类型：" + typeToStr + "，cls：" + (parameterJavaClass == null ? "null" : parameterJavaClass.getFullyQualifiedName()));

                if (parameterJavaClass != null) {

                    // 内置的一些类直接这样生成对象
                    if (InitConstant.FULLY_COLLECTION_VALUE_IMPORT.keySet().contains(typeToStr)) {
                        //设置为ArrayList
                        javaParameterDTO.setSubClassFullyType(InitConstant.FULLY_COLLECTION_VALUE_IMPORT.get(typeToStr));
                        javaParameterDTO.setSubClassType(InitConstant.getAbbreviation(InitConstant.FULLY_COLLECTION_VALUE_IMPORT.get(typeToStr)));

                        javaParameterDTO.setIsInterface(true);
                        log.debug("参数类型为FULLY_COLLECTION_VALUE_IMPORT中类型，javaParameterDTO:" + javaParameterDTO);
                    } else if (parameterJavaClass.isInterface() || parameterJavaClass.isAbstract()) { // 其他接口类型，直接给null
                        javaParameterDTOSetValue(javaParameterDTO, "null");
                    } else if (parameterJavaClass.isEnum()) {
                        setEnumValue(parameterJavaClass, javaParameterDTO);
                    } else {
                        // 有无默认构造器
                        JavaConstructor constructor = parameterJavaClass.getConstructor(List.of());
                        if (constructor != null) {
                            javaParameterDTO.setHasDefaultConstructor(true);
                        }

                        //自定义的类型
                        javaParameterDTO.setCustomType(true);
                        List<JavaParameterDTO> javaParameterDTOList = new ArrayList<>();
                        //获取属性
                        fetchSetterFields(javaParameterDTOList, parameterJavaClass, javaClassInfo);

                        //获取父类属性
                        JavaClass superJavaClass = JavaClassHelper.findSuperClassByQualifiedName(parameterJavaClass.getFullyQualifiedName());
                        if (superJavaClass != null) {
                            do {
                                if (!InitConstant.MAPPING.containsKey(superJavaClass.getFullyQualifiedName())) {
                                    fetchSetterFields(javaParameterDTOList, superJavaClass, javaClassInfo);
                                }
                                log.debug("superJavaClass:" + superJavaClass.getFullyQualifiedName() + ",自定义类型" + typeToStr + "，自定义类型中的类型：" + javaParameterDTOList);

                            } while ((superJavaClass = JavaClassHelper.findSuperClassByQualifiedName(superJavaClass.getFullyQualifiedName())) != null);
                        }
                        javaParameterDTO.setJavaParameterDTOList(javaParameterDTOList);
                    }
                } else {
                    javaParameterDTOSetValue(javaParameterDTO, "null");
                }

                //处理全称限定名称 - 简称
                FullNameHandle.addQualifiedNameToImportPackageMap(javaParameterDTO, javaClassInfo.getImportJavaPackageMap());
            }

            log.debug("生成完参数值的设定:" + javaParameterDTO);
            javaParameterDTOS.add(javaParameterDTO);
        }

        javaMethodDTO.setJavaParameterDTOList(javaParameterDTOS);
    }


    /**
     * 获取所有set的属性，生成mock对象的set方法
     * 如果类上有lombok的注解，所有属性都加入到参数中（属性上有setter注解，不予处理）
     * 如果没有lombok注解，但是有set方法与属性对应，属性加入到参数中
     *
     * @param javaParameterDTOList 模板的属性信息集合
     * @param javaClass            类信息
     * @param javaClassInfo        存储的类信息
     */
    private static void fetchSetterFields(List<JavaParameterDTO> javaParameterDTOList, JavaClass javaClass, JavaClassInfo javaClassInfo) {
        if (javaClass.isInterface()) {
            return;
        }
        //获取方法名称 - set方法名称
        Set<String> methodSet = new HashSet<>();
        List<JavaMethod> javaMethodList = javaClass.getMethods();
        for (JavaMethod javaMethod : javaMethodList) {
            methodSet.add(javaMethod.getName());
        }

        //类上的注解是否有@Setter/@Data注解
        boolean hasAnnotationSet = false;
        List<JavaAnnotation> annotationList = javaClass.getAnnotations();
        for (JavaAnnotation javaAnnotation : annotationList) {
            JavaClass anClass = javaAnnotation.getType();
            String anClassName = anClass.getFullyQualifiedName();
            if (InitConstant.CLASS_ANNOTATION_AUTO_SET.contains(anClassName)) {
                hasAnnotationSet = true;
            }
        }

        List<JavaField> javaFields = javaClass.getFields();

        for (JavaField javaField : javaFields) {
            if (javaField.isStatic()) {
                continue;
            }
            if (javaField.isFinal()) {
                continue;
            }
            //遍历属性,属性名称
            String fieldName = javaField.getName();

            JavaParameterDTO javaParameterDTO = new JavaParameterDTO();
            javaParameterDTO.setUpName(StringUtils.strConvertUpperCamel(fieldName));

            if (!hasAnnotationSet && !methodSet.contains("set" + javaParameterDTO.getUpName())) {
                continue;
            }
            //获取属性类型
            String fullyTypeName = javaField.getType().getFullyQualifiedName();
            String fieldType = InitConstant.getAbbreviation(fullyTypeName);

            javaParameterDTO.setCustomType(false);
            javaParameterDTO.setName(fieldName);
            javaParameterDTO.setType(fieldType);
            javaParameterDTO.setFullyType(fullyTypeName);
            String value = InitConstant.getValue(fieldType);
            javaParameterDTOSetValue(javaParameterDTO, value);

            //全称需要增加到导入的包中
            FullNameHandle.addQualifiedNameToImportPackageMap(javaParameterDTO, javaClassInfo.getImportJavaPackageMap());

            if (value == null) {
                JavaClass fieldJavaClass = JavaClassHelper.findClassByQualifiedName(fullyTypeName);
                if (fieldJavaClass != null) {
                    if (InitConstant.COLLECTION_VALUE_IMPORT.keySet().contains(fieldType)) {
                        //设置为ArrayList
                        javaParameterDTO.setFullyType(InitConstant.COLLECTION_VALUE_IMPORT.get(fieldType));
                        javaParameterDTO.setType(InitConstant.getAbbreviation(InitConstant.COLLECTION_VALUE_IMPORT.get(fieldType)));
                    } else if (fieldJavaClass.isInterface() || fieldJavaClass.isAbstract()) {
                        javaParameterDTOSetValue(javaParameterDTO, "null");
                    } else if (fieldJavaClass.isEnum()) {
                        setEnumValue(fieldJavaClass, javaParameterDTO);
                    }
                    //全称需要增加到导入的包中
                    FullNameHandle.addQualifiedNameToImportPackageMap(javaParameterDTO, javaClassInfo.getImportJavaPackageMap());
                }
            }
            javaParameterDTOList.add(javaParameterDTO);
        }
    }

    /**
     * 设置参数的值
     *
     * @param javaParameterDTO
     * @param value
     */
    private static void javaParameterDTOSetValue(JavaParameterDTO javaParameterDTO, String value) {
        javaParameterDTO.setValue(value);
    }

    /**
     * 设置枚举值
     *
     * @param javaClass
     * @param javaParameterDTO
     */
    private static void setEnumValue(JavaClass javaClass, JavaParameterDTO javaParameterDTO) {
        //枚举取值
        List<JavaField> javaFieldList = javaClass.getFields();
        log.debug("获取的枚举值：" + javaFieldList + "，javaClass=" + javaClass);
        if (!javaFieldList.isEmpty()) {
            JavaField javaField = javaFieldList.get(0);
            javaParameterDTOSetValue(javaParameterDTO, javaClass.getName() + "." + javaField.getName());
        } else {
            javaParameterDTOSetValue(javaParameterDTO, "null");
        }
    }

}
